// 版权所有2021 Go作者。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

// 此文件实现索引/切片表达式的类型检查。

package types2

import (
	"cmd/compile/internal/syntax"
	"go/constant"
)

// 如果e是有效的函数实例化，indexExpr将返回true。
// 在这种情况下，x表示未实例化的函数值，而
// 调用方负责实例化函数。
func (check *Checker) indexExpr(x *operand, e *syntax.IndexExpr) (isFuncInst bool) {
	check.exprOrType(x, e.X)

	switch x.mode {
	case invalid:
		check.use(e.Index)
		return false

	case typexpr:
		// 类型实例化
		x.mode = invalid
		x.typ = check.varType(e)
		if x.typ != Typ[Invalid] {
			x.mode = typexpr
		}
		return false

	case value:
		if sig := asSignature(x.typ); sig != nil && len(sig.tparams) > 0 {
			// 函数实例化
			return true
		}
	}

	// 普通索引表达式
	valid := false
	length := int64(-1) // 如果>=0，则有效
	switch typ := optype(x.typ).(type) {
	case *Basic:
		if isString(typ) {
			valid = true
			if x.mode == constant_ {
				length = int64(len(constant.StringVal(x.val)))
			}
			// 索引字符串总是产生一个字节值
			// （不是常量）即使字符串和
			// 指数是常数
			x.mode = value
			x.typ = universeByte // 使用“字节”名称
		}

	case *Array:
		valid = true
		length = typ.len
		if x.mode != variable {
			x.mode = value
		}
		x.typ = typ.elem

	case *Pointer:
		if typ := asArray(typ.base); typ != nil {
			valid = true
			length = typ.len
			x.mode = variable
			x.typ = typ.elem
		}

	case *Slice:
		valid = true
		x.mode = variable
		x.typ = typ.elem

	case *Map:
		index := check.singleIndex(e)
		if index == nil {
			x.mode = invalid
			return
		}
		var key operand
		check.expr(&key, index)
		check.assignment(&key, typ.key, "map index")
		// 确定即使索引失败也要继续-映射元素类型已知
		x.mode = mapindex
		x.typ = typ.elem
		x.expr = e
		return

	case *Sum:
		// 如果一个和的所有类型
		// 支持索引并具有相同的索引和元素
		// 类型特殊规则适用于总和类型的映射。
		var tkey, telem Type // 键仅适用于地图类型
		nmaps := 0           // 总和类型中的映射类型数
		if typ.is(func(t Type) bool {
			var e Type
			switch t := under(t).(type) {
			case *Basic:
				if isString(t) {
					e = universeByte
				}
			case *Array:
				e = t.elem
			case *Pointer:
				if t := asArray(t.base); t != nil {
					e = t.elem
				}
			case *Slice:
				e = t.elem
			case *Map:
				// 如果sum类型中有多个映射，
				// 它们必须具有相同的键类型。
				// 我们可以放宽这条规定
				// 但它很快变得复杂起来。
				if tkey != nil && !Identical(t.key, tkey) {
					return false
				}
				tkey = t.key
				e = t.elem
				nmaps++
			case *TypeParam:
				check.errorf(x, "type of %s contains a type parameter - cannot index (implementation restriction)", x)
			case *instance:
				panic("unimplemented")
			}
			if e == nil || telem != nil && !Identical(e, telem) {
				return false
			}
			telem = e
			return true
		}) {
			// 如果存在映射，则索引表达式必须是可赋值的
			// 映射键类型（对于简单的映射索引表达式）。
			if nmaps > 0 {
				index := check.singleIndex(e)
				if index == nil {
					x.mode = invalid
					return
				}
				var key operand
				check.expr(&key, index)
				check.assignment(&key, tkey, "map index")
				// 确定即使索引失败也要继续-映射元素类型已知

				// 如果只有地图，我们就完了。
				if nmaps == len(typ.types) {
					x.mode = mapindex
					x.typ = telem
					x.expr = e
					return
				}

				// 否则，我们将混合使用地图和其他类型。对于
				// 现在我们要求map键是整数类型。
				// TODO（gri）这可能还不够好。
				valid = isInteger(tkey)
				// 如果上述索引失败，则避免第二次索引错误
				if !valid && key.mode == invalid {
					x.mode = invalid
					return
				}
				x.mode = value // 映射索引表达式不可寻址
			} else {
				// 没有地图
				valid = true
				x.mode = variable
			}
			x.typ = telem
		}
	}

	if !valid {
		check.errorf(x, invalidOp+"cannot index %s", x)
		x.mode = invalid
		return
	}

	index := check.singleIndex(e)
	if index == nil {
		x.mode = invalid
		return
	}

	// 在病理（无效）病例中（例如：T1[][[]T1{}[0][0]]T0型）
	// 可以在设置元素类型之前访问它。确保我们有
	// 有效类型。
	if x.typ == nil {
		x.typ = Typ[Invalid]
	}

	check.index(index, length)
	return false
}

func (check *Checker) sliceExpr(x *operand, e *syntax.SliceExpr) {
	check.expr(x, e.X)
	if x.mode == invalid {
		check.use(e.Index[:]...)
		return
	}

	valid := false
	length := int64(-1) // 如果>=0，则有效
	switch typ := optype(x.typ).(type) {
	case *Basic:
		if isString(typ) {
			if e.Full {
				check.error(x, invalidOp+"3-index slice of string")
				x.mode = invalid
				return
			}
			valid = true
			if x.mode == constant_ {
				length = int64(len(constant.StringVal(x.val)))
			}
			// spec:“对于非类型化字符串操作数，结果
			// 是字符串类型的非常量值。“
			if typ.kind == UntypedString {
				x.typ = Typ[String]
			}
		}

	case *Array:
		valid = true
		length = typ.len
		if x.mode != variable {
			check.errorf(x, invalidOp+"%s (slice of unaddressable value)", x)
			x.mode = invalid
			return
		}
		x.typ = &Slice{elem: typ.elem}

	case *Pointer:
		if typ := asArray(typ.base); typ != nil {
			valid = true
			length = typ.len
			x.typ = &Slice{elem: typ.elem}
		}

	case *Slice:
		valid = true
		// x、 类型不变

	case *Sum, *TypeParam:
		check.error(x, "generic slice expressions not yet implemented")
		x.mode = invalid
		return
	}

	if !valid {
		check.errorf(x, invalidOp+"cannot slice %s", x)
		x.mode = invalid
		return
	}

	x.mode = value

	// spec：“只能省略第一个索引；它默认为0。”
	if e.Full && (e.Index[1] == nil || e.Index[2] == nil) {
		check.error(e, invalidAST+"2nd and 3rd index required in 3-index slice")
		x.mode = invalid
		return
	}

	// 检查指标
	var ind [3]int64
	for i, expr := range e.Index {
		x := int64(-1)
		switch {
		case expr != nil:
			// “容量”仅静态地用于字符串、数组、，
			// 和指向数组的指针，其长度与
			// 那些类型。
			max := int64(-1)
			if length >= 0 {
				max = length + 1
			}
			if _, v := check.index(expr, max); v >= 0 {
				x = v
			}
		case i == 0:
			// 第一个索引的默认值为0
			x = 0
		case length >= 0:
			// 默认值为长度（=容量），否则为
			x = length
		}
		ind[i] = x
	}

	// 常数索引必须在范围内
	// （check.index已检查现有索引>=0）
L:
	for i, x := range ind[:len(ind)-1] {
		if x > 0 {
			for _, y := range ind[i+1:] {
				if y >= 0 && x > y {
					check.errorf(e, "invalid slice indices: %d > %d", x, y)
					break L // 只报告一个错误，确定继续吗
				}
			}
		}
	}
}

// singleIndex从索引表达式e返回（单个）索引。
// 如果缺少索引，或者存在多个索引，则会出现错误
// 已报告，结果为零。
func (check *Checker) singleIndex(e *syntax.IndexExpr) syntax.Expr {
	index := e.Index
	if index == nil {
		check.errorf(e, invalidAST+"missing index for %s", e.X)
		return nil
	}
	if l, _ := index.(*syntax.ListExpr); l != nil {
		if n := len(l.ElemList); n <= 1 {
			check.errorf(e, invalidAST+"invalid use of ListExpr for index expression %v with %d indices", e, n)
			return nil
		}
		// len（l.ElemList）>1
		check.error(l.ElemList[1], invalidOp+"more than one index")
		index = l.ElemList[0] // 继续第一个索引
	}
	return index
}

// 索引检查索引表达式的有效性。
// 如果max>=0，则为索引的上限。
// 如果结果类型为！=Typ[Invalid]，索引有效，Typ是其（可能命名的）整数类型。
// 如果结果val>=0，则索引有效，val是其常量int值。
func (check *Checker) index(index syntax.Expr, max int64) (typ Type, val int64) {
	typ = Typ[Invalid]
	val = -1

	var x operand
	check.expr(&x, index)
	if !check.isValidIndex(&x, "index", false) {
		return
	}

	if x.mode != constant_ {
		return x.typ, -1
	}

	if x.val.Kind() == constant.Unknown {
		return
	}

	v, ok := constant.Int64Val(x.val)
	assert(ok)
	if max >= 0 && v >= max {
		if check.conf.CompilerErrorMessages {
			check.errorf(&x, invalidArg+"array index %s out of bounds [0:%d]", x.val.String(), max)
		} else {
			check.errorf(&x, invalidArg+"index %s is out of bounds", &x)
		}
		return
	}

	// 0<=v[&&v<max]
	return x.typ, v
}

// isValidIndex检查操作数x是否满足整数的条件
// 索引值。如果设置allowNegative，则常量操作数可能为负数。
// 如果操作数无效，将报告错误（使用什么作为上下文）
// 结果是错误的。
func (check *Checker) isValidIndex(x *operand, what string, allowNegative bool) bool {
	if x.mode == invalid {
		return false
	}

	// 规范：“未类型化的常量索引被赋予int类型”
	check.convertUntyped(x, Typ[Int])
	if x.mode == invalid {
		return false
	}

	// 说明：“索引x必须是整数类型或非类型常量”
	if !isInteger(x.typ) {
		check.errorf(x, invalidArg+"%s %s must be integer", what, x)
		return false
	}

	if x.mode == constant_ {
		// 说明：“常量索引必须是非负的…”
		if !allowNegative && constant.Sign(x.val) < 0 {
			check.errorf(x, invalidArg+"%s %s must not be negative", what, x)
			return false
		}

		// 规范：“…可由int类型的值表示”
		if !representableConst(x.val, check, Typ[Int], &x.val) {
			check.errorf(x, invalidArg+"%s %s overflows int", what, x)
			return false
		}
	}

	return true
}

// indexElts检查数组或切片复合文字的元素（ELT）
// 针对文本的元素类型（typ），元素索引针对
// 文字长度（如果已知）（长度>=0）。它返回的是数据的长度
// 文字（最大索引值+1）。
func (check *Checker) indexedElts(elts []syntax.Expr, typ Type, length int64) int64 {
	visited := make(map[int64]bool, len(elts))
	var index, max int64
	for _, e := range elts {
		// 确定和检查指标
		validIndex := false
		eval := e
		if kv, _ := e.(*syntax.KeyValueExpr); kv != nil {
			if typ, i := check.index(kv.Key, length); typ != Typ[Invalid] {
				if i >= 0 {
					index = i
					validIndex = true
				} else {
					check.errorf(e, "index %s must be integer constant", kv.Key)
				}
			}
			eval = kv.Value
		} else if length >= 0 && index >= length {
			check.errorf(e, "index %d is out of bounds (>= %d)", index, length)
		} else {
			validIndex = true
		}

		// 如果我们有一个有效的索引，请检查重复的条目
		if validIndex {
			if visited[index] {
				check.errorf(e, "duplicate index %d in array or slice literal", index)
			}
			visited[index] = true
		}
		index++
		if index > max {
			max = index
		}

		// 根据复合文字元素类型检查元素
		var x operand
		check.exprWithHint(&x, eval, typ)
		check.assignment(&x, typ, "array or slice literal")
	}
	return max
}
